package GMDPRE

// Sm2KeyGen return public key bytes
func Sm2KeyGen(priKeyBytes []uint8) (int, []uint8) {
	if len(priKeyBytes) != Sm2CurveSize {
		return -1, []uint8{}
	}
	var priKey BN
	var pubKey AffPoint
	var buffer = JacPoint{BN{1}, BN{1}, BN{0}}

	BytesToBn(&priKey, Sm2CurveDigit, priKeyBytes, Sm2CurveSize)

	NumMulJacP2JacP(&buffer, &priKey, &Sm2JacG)
	JacP2AffC(&(pubKey.x), &(pubKey.y), &buffer)

	return 0, AffP2Bytes(&pubKey)
}

// Sm2SignHash testPriKeyBytesStd	- the private key [32 bytes]
// Sm2SignHash rnd					- the random bytes [32 bytes]
// Sm2SignHash hash					- the hash value to be signed [32 bytes]
// @return sig						- the signature [64 bytes]
func Sm2SignHash(priKeyBytes []uint8, rnd []uint8, hash []uint8) (int, []uint8) {
	var sig []uint8
	var ret = 0
	// buffer
	var tmp1, tmp2 BN
	var bufP JacPoint
	// hash value of message
	var e BN
	// private key
	var d BN
	// random number
	var k BN
	// signature
	var r, s BN

	BytesToBn(&e, Sm2CurveDigit, hash, Sm2CurveSize)
	BytesToBn(&d, Sm2CurveDigit, priKeyBytes, Sm2CurveSize)
	BytesToBn(&k, Sm2CurveDigit, rnd, Sm2CurveSize)

	// A4
	// (tmp1, tmp2) = (x1, y1) = kG
	NumMulJacP2JacP(&bufP, &k, &Sm2JacG)
	JacP2AffC(&tmp1, &tmp2, &bufP)

	// A5
	// r = (e + x1) mod n
	BnModAdd(&r, &e, &tmp1, &Sm2N, Sm2CurveDigit)
	BnAddBasic(&tmp2, &r, &k, Sm2CurveDigit)
	/* Shall goto A3 */
	if BnIsZero(&r, Sm2CurveDigit) == 1 || BnEqual(&tmp2, &Sm2N, Sm2CurveDigit) == 1 {
		ret = -1
		goto A8
	}

	// A6:
	// s = [(1+d)^(-1) * (k-r*d)]
	// tmp1 = (1+d) % n
	BnModAdd(&tmp1, &Bn1, &d, &Sm2N, Sm2CurveDigit)
	/* s = (1+d)^(-1) % n*/
	BnMontMul(&tmp1, &tmp1, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit)
	BnMontExp(&s, &tmp1, &Sm2NSub2, &Sm2N, Sm2McN, Sm2CurveDigit)

	/* tmp2 = r*d % n */
	BnMontMul(&tmp1, &r, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit)
	BnMontMul(&tmp2, &d, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit)
	BnMontMul(&tmp2, &tmp1, &tmp2, &Sm2N, Sm2McN, Sm2CurveDigit)
	/* tmp1 = (k - r*d) % n*/
	BnMontMul(&tmp1, &k, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit)
	BnModSub(&tmp1, &tmp1, &tmp2, &Sm2N, Sm2CurveDigit)

	BnMontMul(&s, &s, &tmp1, &Sm2N, Sm2McN, Sm2CurveDigit)
	/* Shall goto A3 */
	if BnIsZero(&s, Sm2CurveDigit) == 1 {
		ret = -2
		goto A8
	}
	BnMontMul(&s, &s, &Bn1, &Sm2N, Sm2McN, Sm2CurveDigit)

	// A7
	sig = BnToBytes(&r, Sm2CurveDigit)
	sig = append(sig, BnToBytes(&s, Sm2CurveDigit)...)

A8:
	return ret, sig
}

func Sm2VrfHashSign(hash []uint8, pubKeyBytes []uint8, sig []uint8) int {
	var ret = 0

	// signature
	var r, s BN

	// hash value
	var e BN

	/* buffer */
	var tmp1, tmp2 BN
	var pubKey JacPoint
	var sG = JacPoint{BN{1}, BN{1}, BN{0}}
	var tP = JacPoint{BN{1}, BN{1}, BN{0}}

	BytesToBn(&tmp1, Sm2CurveDigit, pubKeyBytes[:Sm2CurveSize], Sm2CurveSize)
	BytesToBn(&tmp2, Sm2CurveDigit, pubKeyBytes[Sm2CurveSize:], Sm2CurveSize)
	AffC2JacP(&pubKey, &tmp1, &tmp2)

	// B1
	BytesToBn(&r, Sm2CurveDigit, sig[:Sm2CurveSize], Sm2CurveSize)
	if BnIsZero(&r, Sm2CurveDigit) == 1 || BnCmp(&r, &Sm2N, Sm2CurveDigit) != -1 {
		ret = -1
		goto B8
	}

	// B2
	BytesToBn(&s, Sm2CurveDigit, sig[Sm2CurveSize:], Sm2CurveSize)
	if BnIsZero(&s, Sm2CurveDigit) == 1 || BnCmp(&s, &Sm2N, Sm2CurveDigit) != -1 {
		ret = -1
		goto B8
	}

	// B4
	BytesToBn(&e, Sm2CurveDigit, hash, Sm2CurveSize)

	// B5
	BnModAdd(&tmp1, &r, &s, &Sm2N, Sm2CurveDigit)
	if BnIsZero(&tmp1, Sm2CurveDigit) == 1 {
		ret = -2
		goto B8
	}

	// B6
	NumMulJacP2JacP(&sG, &s, &Sm2JacG)
	NumMulJacP2JacP(&tP, &tmp1, &pubKey)

	JacPAddJacP2JacP(&tP, &sG, &tP)

	// (tmp1, tmp2) = (x1, x2) = sG+tP
	JacP2AffC(&tmp1, &tmp2, &tP)

	// B7
	BnModAdd(&tmp2, &e, &tmp1, &Sm2N, Sm2CurveDigit)
	if BnCmp(&tmp2, &r, Sm2CurveDigit) != 0 {
		ret = -3
		goto B8
	}

B8:
	return ret
}
